package com.zmn.plat.business.impl.price;

import com.google.common.collect.Lists;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dto.KvDTO;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.plat.business.interfaces.channel.ChannelServiceCategoryBService;
import com.zmn.plat.business.interfaces.pilot.ChannelPriceBService;
import com.zmn.plat.business.interfaces.price.BkProductPriceBService;
import com.zmn.plat.business.interfaces.price.CommonBService;
import com.zmn.plat.business.interfaces.product.ServProductBService;
import com.zmn.plat.common.constant.OnePriceConsts;
import com.zmn.plat.common.constant.ProductConsts;
import com.zmn.plat.common.dto.price.ItemTo;
import com.zmn.plat.common.dto.price.ProductPriceDRO;
import com.zmn.plat.common.dto.price.ProductPriceQuery;
import com.zmn.plat.common.exception.PlatException;
import com.zmn.plat.manager.dictionary.ProductDict;
import com.zmn.plat.model.dto.price.PriceCalDTO;
import com.zmn.plat.model.dto.product.ProductRelationDTO;
import com.zmn.plat.model.entity.pilot.SupportPilotVO;
import com.zmn.plat.model.entity.product.ServProduct;
import com.zmn.plat.model.entity.product.ServProductQuery;
import com.zmn.plat.model.entity.product.tariff.ProductTariffQuery;
import com.zmn.plat.model.entity.product.tariff.item.ProductItemQuery;
import com.zmn.plat.model.entity.service.category.ItemCategory;
import com.zmn.plat.model.entity.service.group.ServiceGroup;
import com.zmn.plat.model.entity.service.tariff.ServiceTariff;
import com.zmn.plat.model.vo.product.tariff.ProductTariffVO;
import com.zmn.plat.services.interfaces.product.ServProductService;
import com.zmn.plat.services.interfaces.product.tariff.ProductTariffService;
import com.zmn.plat.services.interfaces.product.tariff.item.ProductTariffGroupItemService;
import com.zmn.plat.services.interfaces.product.tariff.item.city.price.ProductTariffItemCityPriceService;
import com.zmn.plat.services.interfaces.service.group.ServiceGroupService;
import com.zmn.plat.services.interfaces.service.tariff.ServiceTariffService;
import com.zmn.plat.utils.bean.BeanCopyUtils;
import com.zmn.plat.utils.bean.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 描述: 产品销售价
 *
 * @author guoqiao
 * @since 2021/8/27 15:32
 */
@Service
@Slf4j
public class BkProductPriceBServiceImpl implements BkProductPriceBService {
    @Resource
    ServProductBService servProductBService;
    @Resource
    ServProductService servProductService;
    @Resource
    ChannelPriceBService channelPriceBService;
    @Resource
    CommonBService commonBService;
    @Resource
    ChannelServiceCategoryBService channelServiceCategoryBService;
    @Resource
    ServiceTariffService serviceTariffService;
    @Resource
    ProductTariffGroupItemService productTariffGroupItemService;
    @Resource
    ProductTariffItemCityPriceService productTariffItemCityPriceService;
    @Resource
    ProductTariffService productTariffService;
    @Resource
    ServiceGroupService serviceGroupService;

    // 分批次处理的数量
    private final static int DEFAULT_PAGE_SIZE = 1;
    // 开启的线程池核心线程数
    private final static int CORE_POOL_SIZE = 5;
    // 开启的线程池最大线程数
    private final static int MAX_POOL_SIZE = 6;
    // 开启的线程池闲置时间
    private final static int KEEP_ALIVE_TIME = 10;

    @Override
    @Cacheable(cacheNames = "redis10m", key = "'price:listProductPriceByQuery:ProductPriceQuery:'+#p0", unless = "#result == null")
    public List<ProductPriceDRO> listProductPriceByQuery(ProductPriceQuery query) {
        List<Integer> productIdList = query.getProductIdList();
        // 处理完毕之后的数据
        List<ProductPriceDRO> droList = new ArrayList<>();
        // 待处理的数据分批次处理
        List<ProductPriceQuery> queryList = new ArrayList<>();
        // 内存分页，构造分批次的数据
        int totalCount = productIdList.size();
        int page = totalCount % DEFAULT_PAGE_SIZE == 0 ? totalCount / DEFAULT_PAGE_SIZE : totalCount / DEFAULT_PAGE_SIZE + 1;
        for (int i = 1; i <= page; i++) {
            ProductPriceQuery sub = new ProductPriceQuery();
            BeanCopyUtils.copyProperties(query, sub);
            sub.setProductIdList(PageUtils.listPage(productIdList, i, DEFAULT_PAGE_SIZE));
            queryList.add(sub);
        }

        // 开启多线程，以当前处理的页数为队列长度
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new ArrayBlockingQueue<>(productIdList.size()));
        CompletableFuture<List<ProductPriceDRO>>[] completableFutures = null;
        try {
            completableFutures = queryList.stream().map(excuteQuery -> CompletableFuture.supplyAsync(() -> doExcuteHander(excuteQuery), threadPoolExecutor)).toArray(CompletableFuture[]::new);
            // 等待所有任务执行完
            CompletableFuture.allOf(completableFutures).join();
            // 归并返回值
            for (CompletableFuture<List<ProductPriceDRO>> completableFuture : completableFutures) {
                droList.addAll(completableFuture.get());
            }
        } catch (Exception e) {
            log.error("多线程跑价格计算失败：MSG:{}", e.getMessage(), e);
        } finally {
            // 关闭线程池
            threadPoolExecutor.shutdown();
        }
        return droList;
    }

    private List<ProductPriceDRO> doExcuteHander(ProductPriceQuery query) {

        List<Integer> productIdList = query.getProductIdList();
        Integer channelId = query.getChannelId(); // 原始渠道id,试点使用该字段
        Integer cityId = query.getCityId();
        Integer showType = query.getShowType();
        Integer bizType = query.getBizType();

        // 判断渠道是否开通分类，如果已开通则取当前渠道，反之取上级渠道信息
        Integer avaliableChannelId = channelId;
        try {
            avaliableChannelId = channelServiceCategoryBService.getAvailableCategoryChannelId(channelId);
            log.info("原渠道：{}，可用渠道：{}", channelId, avaliableChannelId);
            query.setChannelId(avaliableChannelId);
        } catch (PlatException e) {
            log.error("无可用渠道，全部30元起");
            return this.createNotSupportOnePriceDRO(new HashSet<>(productIdList), null);
        }

        // 出参集合
        List<ProductPriceDRO> droList = new ArrayList<>(productIdList.size());

        // 查询前台、后台、计价器产品关系
        List<ProductRelationDTO> relationDTOList = Optional.ofNullable
                (servProductBService.listProductRalationByIdListAndShowType(productIdList, showType))
                .orElse(Collections.emptyList());
        Map<Integer, ProductRelationDTO> relationDTOMap = relationDTOList.stream().collect(Collectors.toMap(e -> e.getCurrentProductId(), e -> e, (p, v) -> p));
        log.info("前台、后台、计价器产品关系:{}", relationDTOList);

        // 查询价格表
        commonBService.getTariffByPilot(relationDTOList, channelId, cityId, bizType);
        log.info("计算后的产品关系价格表relationDTOList：{}", relationDTOList);

        // 筛选出价格表优先的数据
        commonBService.filterTariff(relationDTOList, showType, false);
        log.info("筛选出价格表优先的数据relationDTOFilterList：{}", relationDTOList);

        // 产品与价格表id的关系
        Map<Integer, Integer> productTariffMap = relationDTOList.stream().collect(Collectors.toMap(ProductRelationDTO::getCurrentProductId, ProductRelationDTO::getTariffId, (k, v) -> k));

        // 处理定金
        this.handleEarnest(productIdList, channelId, cityId, relationDTOList, droList);

        // 处理前后台产品、计价器后台产品一对一的关系
        this.handleOneToOne(relationDTOList, avaliableChannelId);
        log.info("处理一对一关系后,前台、后台、计价器产品关系:{}", relationDTOList);

        // 计算价格、获取项目
        this.handelTariff(relationDTOList, query, droList);

        // 处理没有价格表的数据为30元起
        List<Integer> droProductIdList = droList.stream().map(e -> e.getProductId()).collect(Collectors.toList());
        Set<Integer> set = productIdList.stream().filter(e -> !droProductIdList.contains(e)).collect(Collectors.toSet());
        droList.addAll(this.createNotSupportOnePriceDRO(set, productTariffMap));

        // 支付方式
        droList.forEach(e -> {
            if (NumberUtil.isNullOrZero(e.getPayType())) {
                ProductRelationDTO relationDTO = relationDTOMap.get(e.getProductId());
                if (Objects.nonNull(relationDTO)) {
                    SupportPilotVO supportPilotVO = channelPriceBService.listPilotCategs(channelId, cityId, OnePriceConsts.ONE_PRICE, relationDTO.getShowServCategId(), relationDTO.getShowCategId());
                    e.setPayType(Objects.nonNull(supportPilotVO) ? supportPilotVO.getPayType() : null);
                }
            }
        });
        return droList;
    }

    /**
     * 处理定金
     */
    private void handleEarnest(List<Integer> productIdList, Integer channelId, Integer cityId, List<ProductRelationDTO> relationDTOList, List<ProductPriceDRO> droList) {
        // 默认产品（没有与后台产品建立关联关系）
        ServProductQuery servProductQuery = new ServProductQuery();
        servProductQuery.setProductIds(productIdList);
        servProductQuery.setDefaultType(GlobalConsts.YES);
        List<ServProduct> defaultProductList = servProductService.listByQuery(servProductQuery);

        // 已处理产品id集合
        List<Integer> handledProductIds = new ArrayList<>();
        // 默认产品的试点
        for (ServProduct productFt : defaultProductList) {
            SupportPilotVO supportPilotVO = channelPriceBService.listPilotCategs(channelId, cityId, OnePriceConsts.ONE_PRICE, productFt.getServCategId(), productFt.getCategId());
            if (Objects.nonNull(supportPilotVO) && NumberUtil.isNotNullOrZero(supportPilotVO.getEarnestPrice()) && !CollectionUtils.isEmpty(supportPilotVO.getServCategIdList())) {
                ProductPriceDRO productPriceDRO = new ProductPriceDRO();
                handledProductIds.add(productFt.getProductId());
                productPriceDRO.setProductId(productFt.getProductId());
                productPriceDRO.setPayType(supportPilotVO.getPayType());
                productPriceDRO.setFixedPrice(GlobalConsts.YES);
                productPriceDRO.setEarnestPrice(supportPilotVO.getEarnestPrice());
                droList.add(productPriceDRO);
            }
        }
        // 有关系产品的试点
        for (ProductRelationDTO dto : relationDTOList) {
            SupportPilotVO supportPilotVO = channelPriceBService.listPilotCategs(channelId, cityId, OnePriceConsts.ONE_PRICE, dto.getShowServCategId(), dto.getShowCategId());
            if (Objects.nonNull(supportPilotVO) && NumberUtil.isNotNullOrZero(supportPilotVO.getEarnestPrice()) && !CollectionUtils.isEmpty(supportPilotVO.getServCategIdList())) {
                if (!handledProductIds.contains(dto.getCurrentProductId())) {
                    ProductPriceDRO productPriceDRO = new ProductPriceDRO();
                    handledProductIds.add(dto.getCurrentProductId());
                    productPriceDRO.setProductId(dto.getCurrentProductId());
                    productPriceDRO.setPayType(supportPilotVO.getPayType());
                    productPriceDRO.setFixedPrice(GlobalConsts.YES);
                    productPriceDRO.setEarnestPrice(supportPilotVO.getEarnestPrice());
                    productPriceDRO.setTariffId(dto.getTariffId());
                    productPriceDRO.setTariffType(dto.getTariffType());
                    droList.add(productPriceDRO);
                }
            }
        }
        // 排除掉定金的产品数据
        List<ProductRelationDTO> newDTOList = relationDTOList.stream().filter(e -> !handledProductIds.contains(e.getCurrentProductId())).collect(Collectors.toList());
        relationDTOList.clear();
        relationDTOList.addAll(newDTOList);
    }

    /**
     * 描述：处理前后台产品、计价器后台产品一对一的关系
     */
    private void handleOneToOne(List<ProductRelationDTO> relationDTOList, Integer channelId) {
        if (CollectionUtils.isEmpty(relationDTOList)) {
            return;
        }
        // 查询渠道后端产品
        ServProductQuery query = new ServProductQuery();
        query.setStatus(GlobalConsts.YES);
        query.setChannelId(channelId);
        query.setShowType(ProductConsts.ERP_PRODUCT_TYPE);
        query.setProductType(ProductDict.CHANNEL_PRODUCT);
        List<Integer> channelProductIds = Optional.ofNullable(servProductService.listByQuery(query))
                .orElse(Collections.emptyList())
                .stream()
                .map(ServProduct::getProductId)
                .collect(Collectors.toList());

        // 前后台产品一对一关系的数据
        List<ProductRelationDTO> oneToOneList = relationDTOList.stream()
                .collect(Collectors.groupingBy(ProductRelationDTO::getShowProductId))
                .values()
                .stream()
                .filter(e -> {
                    // 渠道产品
                    List<ProductRelationDTO> collect1 = e.stream()
                            .filter(m -> ProductDict.CHANNEL_PRODUCT == m.getProductType())
                            .filter(m -> channelProductIds.contains(m.getProductId()))
                            .collect(Collectors.toList());
                    if (collect1.size() > 1) {
                        return false;
                    } else if (collect1.size() == 1) {
                        e.clear();
                        e.add(collect1.get(0));
                        return true;
                    }
                    // 通用产品
                    List<ProductRelationDTO> collect2 = e.stream()
                            .filter(m -> ProductDict.CURRENCY_PRODUCT == m.getProductType())
                            .collect(Collectors.toList());
                    if (collect2.size() > 1) {
                        return false;
                    } else if (collect2.size() == 1) {
                        e.clear();
                        e.add(collect2.get(0));
                        return true;
                    }
                    return false;

                })
                .flatMap(Collection::stream).collect(Collectors.toList());

        if (Objects.equals(ProductConsts.ET_PRODUCT_TYPE, query.getShowType())) {
            // 计价器产品需额外检查计价器产品和后台产品的一对一关系
            oneToOneList = oneToOneList.stream().collect(Collectors.groupingBy(ProductRelationDTO::getTariffProductId))
                    .values().stream().filter(e -> e.size() == 1).flatMap(Collection::stream).collect(Collectors.toList());
            // 状态
            oneToOneList = oneToOneList.stream().filter(e -> Objects.equals(e.getTariffProductStatus(), GlobalConsts.YES)
                    && Objects.equals(e.getTariffStatus(), GlobalConsts.YES)).collect(Collectors.toList());
        }
        relationDTOList.clear();
        relationDTOList.addAll(oneToOneList);
    }

    /**
     * 描述：计算价格
     */
    private void handelTariff(List<ProductRelationDTO> relationDTOList, ProductPriceQuery query, List<ProductPriceDRO> droList) {
        if (CollectionUtils.isEmpty(relationDTOList)) {
            return;
        }
        Map<Integer, ProductRelationDTO> relationDTOMap = relationDTOList.stream().collect(Collectors.toMap(e -> e.getProductId(), e -> e, (p, v) -> p));

        // 走系数定价
        List<KvDTO<Integer, Integer>> listRatio = new ArrayList<>(relationDTOList.size());
        // 走城市定价
        List<KvDTO<Integer, Integer>> listCity = new ArrayList<>(relationDTOList.size());
        for (ProductRelationDTO relationDTO : relationDTOList) {
            if (NumberUtil.isNullOrZero(relationDTO.getTariffId())) {
                continue;
            }
            // 查询价格表
            ServiceTariff tariff = serviceTariffService.findByKeyWithCache(relationDTO.getTariffId());
            if (Objects.isNull(tariff)) {
                continue;
            }
            KvDTO kvDTO = new KvDTO(relationDTO.getProductId(), relationDTO.getTariffId());
            if (Objects.equals(tariff.getPriceType(), ProductDict.TRIFF_PRICE_TYPE_CITY)) {
                listCity.add(kvDTO);
            } else {
                listRatio.add(kvDTO);
            }
        }
        // 查询item集合
        List<ItemTo> itemToList = new ArrayList<>(relationDTOList.size());
        ProductItemQuery itemQuery = new ProductItemQuery();
        itemQuery.setGeneral(GlobalConsts.YES);
        if (CollectionUtils.isNotEmpty(listRatio)) {
            // 系数定价item
            itemQuery.setKvList(listRatio);
            List<ItemTo> itemTos = productTariffGroupItemService.listItemPriceByQuery(itemQuery);
            if (itemTos != null) {
                itemToList.addAll(itemTos);
            }
        }
        if (CollectionUtils.isNotEmpty(listCity)) {
            // 城市定价item
            itemQuery.setKvList(listCity);
            itemQuery.setCityId(query.getCityId());
            List<ItemTo> itemTos = productTariffItemCityPriceService.listItemPriceByQuery(itemQuery);
            if (itemTos != null) {
                itemToList.addAll(itemTos);
            }
        }
        log.info("项目itemToList:{}", itemToList);

        PriceCalDTO calDTO = new PriceCalDTO(query.getChannelId(), query.getCityId(), query.getBrandId(), query.getBizType(), null);

        for (ItemTo itemTo : itemToList) {
            // 设置定价方式(计算价格前需设置定价方式)
            ServiceTariff tariff = serviceTariffService.findByKeyWithCache(itemTo.getTariffId());
            itemTo.setTariffName(tariff.getTariffName());
            itemTo.setTariffType(tariff.getTariffType());
            itemTo.setPriceType(tariff.getPriceType());

            // 计算单价
            commonBService.calPriceDetail(itemTo, calDTO);
            log.info("项目itemTo计算价格后数据:{}", itemTo);

            // 转换出参
            ProductPriceDRO productPriceDRO = this.setFixedPriceMsg(itemTo, relationDTOMap.get(itemTo.getProductId()), query.getShowType());
            droList.add(productPriceDRO);
        }
    }

    /**
     * 描述：设置填充属性并返回出参实体
     */
    private ProductPriceDRO setFixedPriceMsg(ItemTo itemTo, ProductRelationDTO relationDTO, Integer showType) {

        itemTo.setProductName(relationDTO.getProductName());
        itemTo.setCategOneName(relationDTO.getCategOneName());
        itemTo.setCategId(relationDTO.getCategId());
        itemTo.setCategName(relationDTO.getCategName());

        // 查询后台产品与价格表关系
        ProductTariffQuery productTariffQuery = new ProductTariffQuery();
        productTariffQuery.setProductId(itemTo.getProductId());
        productTariffQuery.setTariffId(itemTo.getTariffId());
        productTariffQuery.setStatus(GlobalConsts.YES);
        ProductTariffVO productTariff = productTariffService.listByQueryWithCache(productTariffQuery)
                .stream()
                .findFirst()
                .orElse(new ProductTariffVO());
        itemTo.setDesc(productTariff.getDesc());
        itemTo.setSpecialDesc(productTariff.getSpecialDesc());

        // 查询项目分组
        ServiceGroup group = serviceGroupService.findByKey(itemTo.getGroupId());
        itemTo.setGroupName(group.getGroupName());
        itemTo.setGroupSort(group.getSort());

        ProductPriceDRO productPriceDRO = new ProductPriceDRO();
        productPriceDRO.setProductId(relationDTO.getCurrentProductId());
        productPriceDRO.setPrice(itemTo.getPrice());
        productPriceDRO.setExternalSettlementPrice(itemTo.getExternalSettlementPrice());
        productPriceDRO.setInternalSettlementPrice(itemTo.getInternalSettlementPrice());
        productPriceDRO.setFixedPrice(GlobalConsts.YES);
        productPriceDRO.setItemId(itemTo.getItemId());
        productPriceDRO.setTariffId(itemTo.getTariffId());
        productPriceDRO.setTariffType(itemTo.getTariffType());

        if (Objects.equals(ProductDict.EC_PRODUCT_TYPE, showType)) {
            itemTo.setProductName(relationDTO.getShowProductName());
            itemTo.setCategOneName(relationDTO.getShowCategOneName());
            itemTo.setCategName(relationDTO.getShowCategName());
        } else if (Objects.equals(ProductDict.ERP_PRODUCT_TYPE, showType)) {
            itemTo.setProductName(relationDTO.getProductName());
            itemTo.setCategOneName(relationDTO.getCategOneName());
            itemTo.setCategName(relationDTO.getCategName());
        }
        productPriceDRO.setItemTo(itemTo);
        return productPriceDRO;
    }

    /**
     * 返回30元起的实体集合（非一口价）
     */
    private List<ProductPriceDRO> createNotSupportOnePriceDRO(Set<Integer> productIds, Map<Integer, Integer> productTariffMap) {
        if (CollectionUtils.isNotEmpty(productIds)) {
            return productIds.stream().map(e -> {
                ProductPriceDRO dro = new ProductPriceDRO();
                dro.setProductId(e);
                dro.setFixedPrice(GlobalConsts.NO);
                if (!org.springframework.util.CollectionUtils.isEmpty(productTariffMap) && NumberUtil.isNotNullOrZero(productTariffMap.get(e))) {
                    ServiceTariff tariff = serviceTariffService.findByKeyWithCache(productTariffMap.get(e));
                    dro.setTariffId(productTariffMap.get(e));
                    dro.setTariffType(tariff.getTariffType());
                }
                return dro;
            }).collect(Collectors.toList());
        }
        return Lists.newArrayList();
    }


}
